package dk.itu.mario.level;

import java.util.Random;
import java.util.ArrayList;

import dk.itu.mario.MarioInterface.Constraints;
import dk.itu.mario.MarioInterface.GamePlay;
import dk.itu.mario.MarioInterface.LevelInterface;
import dk.itu.mario.engine.sprites.BulletBill;
import dk.itu.mario.engine.sprites.SpriteTemplate;
import dk.itu.mario.engine.sprites.Enemy;

import dk.itu.mario.engine.JFugue.*;
import org.jfugue.elements.Note;

public class MyLevel extends Level {
	// Store information about the level
	public int ENEMIES = 0; // the number of enemies the level contains
	public int BLOCKS_EMPTY = 0; // the number of empty blocks
	public int BLOCKS_COINS = 0; // the number of coin blocks
	public int BLOCKS_POWER = 0; // the number of power blocks
	public int COINS = 0; // These are the coins in boxes that Mario collect

	private static Random levelSeedRandom = new Random();
	public static long lastSeed;

	Random random;

	private int difficulty;
	private int richness;
	private int type;
	private int gaps;

	private JFugueMidiImporter midiImport;

	public MyLevel(int width, int height) {
		super(width, height);
	}

	public MyLevel(int width, int height, long seed, int difficulty,
			int richness, int type, GamePlay playerMetrics,
			JFugueMidiImporter midiImport) {
		this(width + 64, height);

		this.difficulty = difficulty;
		this.richness = richness;

		this.midiImport = midiImport;
		creat(seed, difficulty, type);
	}

	/**
	 * @return the difficulty
	 */
	public int getDifficulty() {
		return difficulty;
	}

	/**
	 * @param difficulty
	 *            the difficulty to set
	 */
	public void setDifficulty(int difficulty) {
		this.difficulty = difficulty;
	}

	/**
	 * @return the richness
	 */
	public int getRichness() {
		return richness;
	}

	/**
	 * @param richness
	 *            the richness to set
	 */
	public void setRichness(int richness) {
		this.richness = richness;
	}

	public void creat(long seed, int difficulty, int type) {
		ArrayList<Integer> scaledData = new ArrayList<Integer>();
		ArrayList<Integer> terrain = new ArrayList<Integer>();
		ArrayList<Byte> notes = new ArrayList<Byte>();
		ArrayList<Integer> xy = new ArrayList<Integer>();
		ArrayList<Integer> xOff = new ArrayList<Integer>();
		ArrayList<Integer> xFlatSize = new ArrayList<Integer>();
		ArrayList<ArrayList<Integer>> heightMaps = new ArrayList<ArrayList<Integer>>();
		this.type = type;
		this.difficulty = difficulty;

		ArrayList<LayeredNote> music = new ArrayList<LayeredNote>();
		music = midiImport.getNoteListForVoice(0);
		// music = midiImport.getParserMario().getTemporalUnits();
		//Get HeightMaps for others.
		terrain = mBuildNormalizedHeightMap(music);
		//System.out.println("Size="+terrain.size()+" Voice=0");
		for(int i=1; i<9; i++){
			ArrayList<LayeredNote> m= midiImport.getNoteListForVoice(i);
			ArrayList<Integer> hm = mBuildNormalizedHeightMap(m);
			if(hm.size()!=0){
			//	System.out.println("Size="+hm.size()+" Voice="+i);
				heightMaps.add(hm);
			}
		}
		lastSeed = seed;
		random = new Random(seed);
		int count = 0;
		long timelimit = 100;
		/* set blocks according to drums */
		MusicParserForMarioLevelGen drums = new MusicParserForMarioLevelGen(
				midiImport.getNoteListForVoice(9));
		ArrayList<Boolean> block = new ArrayList<Boolean>();

		// separate drum notes into time slices
		count = 0;
		for (LayeredNote layer : drums.getNoteListForDrumInstrument(LayeredNote
				.PercNameFromValue(36))) {
			byte NoteValue = layer.getNote(0).getValue();
			long NoteTime = layer.getTime();
			// System.out.println(NoteTime+" "+timelimit);

			if (NoteTime <= timelimit && NoteValue != 0) {
				count++;
			} else {
				do {
					if (count == 0)
						block.add(false);
					else
						block.add(true);
					timelimit += 100;
					count = 0;
				} while (NoteTime > timelimit);
			}
		}

		// repeat block data till it can fill terrain
		ArrayList<Boolean> temp = block;
		while (block.size() < terrain.size()) {
			block.addAll(temp);
		}

		width = terrain.size() + 64;
		map = new byte[width][height];
		spriteTemplates = new SpriteTemplate[width][height];

		// set up terrain
		if (terrain.size() == 0) {
			System.out.println("ERROR! Empty Terrain ArrayList");
		}
		int prevHeight = terrain.get(0);
		int blockDist = 5;
		int coinDist = 5;
		for (int x = 0; x < terrain.size(); x++) {

			int ht = terrain.get(x);
			if (ht - prevHeight > 3)
				ht += 3;

			if (x > coinDist && x < terrain.size() - 10) {
				int flat = 1;
				for (int k = x + 1; k <= x + 10; k++) {
					if (terrain.get(k) == ht)
						flat++;
					else
						break;
				}
				coinDist = x + flat;
				if (flat >= 8 && random.nextInt(100) < richness) {
					int s = random.nextInt(4);
					int e = random.nextInt(4);

					mSetCoins(x + s, ht, flat - s - e);

				}
			}
			xy.add(ht);
			mBuildPlain(x, ht);
			
			if (x > blockDist && x < terrain.size() - 10) {
				int flat = 1;
				int snarecount = 0;
				for (int k = x + 1; k <= x + 10; k++) {
					if (terrain.get(k) == ht) {
						flat++;
						if (block.get(x))
							snarecount++;
					} else
						break;
				}
				blockDist = x + flat;
				if (flat >= 8
						&& ((snarecount > flat / 2 && random.nextInt(2) == 0) || random
								.nextInt(4) == 0)) {
					int s = random.nextInt(4);
					int e = random.nextInt(4);

					mSetBlocks(x + s, ht, flat - s - e);

				}
			}

			prevHeight = ht;
		}

		// Add gaps, jumps, stairs and hills for extra fun!
		// Check for the flats greater than 1.
		int tempY = xy.get(0);
		int flatCount = 0;
		for (int i = 0; i < xy.size(); i++) {
			if (xy.get(i) == tempY && !hasBlocks(i)) {
				flatCount++;
			} 
			else 
			{
				if (flatCount > 1) 
				{
					xOff.add(i - flatCount);
					xFlatSize.add(flatCount);
					// System.out.println("FLAT From:"+(i-flatCount)+" to"+i);
				}
				flatCount = 0;
			}
			tempY = xy.get(i);
		}
		mBuildFlat(xOff, xy, xFlatSize,heightMaps);
		
		for(int x = 0; x < terrain.size(); x++)
		{
			int ht = terrain.get(x);
			if (ht - prevHeight > 3)
				ht += 3;
			
			if (x > 10)
				mAddEnemy(x, ht);
			
			prevHeight = ht;
		}
		
		//Avoid gaps at the beginning of the level
		for (int x = 0; x<3; x++){
			setBlock(x,xy.get(x),GROUND);
		}
		// Generate the modified flat.
		// set the end piece
		int floor = height - 1 - random.nextInt(4);
		int length = terrain.size();

		xExit = length + 8;
		yExit = floor;

		// fills the end piece
		for (int x = length; x < width; x++) {
			for (int y = 0; y < height; y++) {
				if (y >= floor) {
					setBlock(x, y, GROUND);
				}
			}
		}

		if (type == LevelInterface.TYPE_CASTLE
				|| type == LevelInterface.TYPE_UNDERGROUND) {
			int ceiling = 0;
			int run = 0;
			for (int x = 0; x < width; x++) {
				if (run-- <= 0 && x > 4) {
					ceiling = random.nextInt(4);
					run = random.nextInt(4) + 4;
				}
				for (int y = 0; y < height; y++) {
					if ((x > 4 && y <= ceiling) || x < 1) {
						setBlock(x, y, GROUND);
					}
				}
			}
		}

		fixWalls();
		System.out.println(COINS);

	}

	// Function builds a plain at the x-location specified at height
	// ht from the ground
	private void mBuildPlain(int x, int ht) {
		// System.out.println("Testing!");
		for (int y = height - 1; y > height - ht - 1; y--) {
			setBlock(x, y, GROUND);
		}
		return;
	}

	// Function adds a random enemy at location x given the height
	// ht of the floor
	private void mAddEnemy(int x, int ht) {
		int y = height - ht - 1;

		if(getBlock(x, ht) != 0)
		{
			return;
		}
		
		if (random.nextInt(200) < difficulty + 1) {
			int type = random.nextInt(4);
			Boolean isCannonSet = false;
			
			if (difficulty < 5) {
				type = Enemy.ENEMY_GOOMBA;
			} else if (difficulty < 30) {
				type = random.nextInt(3);
			}
			
			if (difficulty > 0 && random.nextInt(50) < difficulty + 1) {
				mbuildCannons(x, y, 3);
			}
			else
			{
				setSpriteTemplate(x, y, new SpriteTemplate(type,
						random.nextInt(50) < difficulty));
				ENEMIES++;
			}
		}
	}

	private void mbuildCannons(int x, int ya, int maxHeight) {
		int cannonHeight = ya - random.nextInt(maxHeight);

		for (int y = cannonHeight; y <= ya; y++) {
			if (y == cannonHeight) {
				setBlock(x, y, (byte) (14 + 0 * 16));
			} else if (y == cannonHeight + 1) {
				setBlock(x, y, (byte) (14 + 1 * 16));
			} else {
				setBlock(x, y, (byte) (14 + 2 * 16));
			}
		}
	}

	// Inserts coin at the given x location at given height
	private void mSetCoins(int x, int ht, int length) {
		int y = height - ht - 2;
		for (int i = x; i < x + length; i++) {
			setBlock(i, y, COIN);
			COINS++;
		}
	}

	// Inserts n rocks vertically at given x position from the floor at height
	// ht
	private void mSetRocks(int x, int ht, int n) {
		int floor = height - ht - 1;

		for (int y = floor; y > floor - n; y--) {
			setBlock(x, y, ROCK);
		}
	}

	private void mSetBlocks(int x, int y, int length) {
		for (int i = x; i < x + length; i++) {
			int type = random.nextInt(25);
			if (type == 0) {
				setBlock(i, height - y - 4, BLOCK_POWERUP);
			} else if (type <= 2) {
				setBlock(i, height - y - 4, BLOCK_EMPTY);
			} else {
				setBlock(i, height - y - 4, BLOCK_COIN);
			}

			if (getBlock(i, height - y - 2) == COIN) {
				setBlock(i, height - y - 2, (byte) 0);
				if (y < height - 8)
					setBlock(i, height - y - 6, COIN);
			}
		}
	}

	private void mBuildPipes(int x, int y, int ht) {
		// System.out.println("height="+height+" y="+y+" hT="+ht);
		// System.out.println("Aqui empieza el tubo:"+(height-y)+ht);
		int inter = height - y - ht;
		// System.out.println(inter);
		setBlock(x, inter, TUBE_TOP_LEFT);
		setBlock(x + 1, inter, TUBE_TOP_RIGHT);
		for (int i = inter + 1; i < inter + ht; i++) {
			setBlock(x, i, TUBE_SIDE_LEFT);
			setBlock(x + 1, i, TUBE_SIDE_RIGHT);
		}
		setSpriteTemplate(x,inter,new SpriteTemplate(4,false));
	}

	private void mBuildHills(int x, int y, int xh, int yh) {
		int y0 = height - y - yh;
		
		if(getBlock(x, y0) == (byte)(0 + 0 * 16) || getBlock(x, y0) == COIN)
		{
			setBlock(x, y0, HILL_TOP_LEFT);
		}
		
		if(getBlock(x + xh, y0) == (byte)(0 + 0 * 16) || getBlock(x + xh, y0) == COIN)
		{
			setBlock(x + xh, y0, HILL_TOP_RIGHT);
		}
		
		for (int i = y0; i < height; i++) {
			if(getBlock(x, i) == (byte)(0 + 0 * 16) || getBlock(x, i) == COIN || (i > y0 && i <= y))
			{
				setBlock(x, i, HILL_LEFT);
			}
			if(getBlock(x + xh, i) == (byte)(0 + 0 * 16) || getBlock(x + xh, i) == COIN || (i > y0 && i <= y))
			{
				setBlock(x + xh, i, HILL_RIGHT);
			}
			for (int j = 1; j < xh; j++) {
				if(getBlock(x + j, i) == (byte)(0 + 0 * 16) || getBlock(x + j, i) == COIN || (i > y0 && i <= y))
				{
					if(i == y0) {
						setBlock(x + j, i, HILL_TOP);
					}
					else {
						setBlock(x + j, i, HILL_FILL);
					}
				}
			}
		}
	}

	private void mBuildStairs(int x, int y, int size, boolean reversed) {
		if (!reversed) {
			int iter = size - 1;
			for (int i = x; i < x + size; i++) {
				for (int j = 0; j < size - iter; j++) {
					setBlock(i, (height - y) - j + 1, ROCK);
				}
				iter--;
			}
		} else {
			int iter = 0;
			for (int i = x; i < x + size; i++) {
				for (int j = size; j > 0 + iter; j--) {
					setBlock(i, (height - y) - j + iter, ROCK);
				}
				iter++;
			}
		}
	}

	private void mBuildSmallGap(int x, int y) {
		int gapSize = random.nextInt(3) + 1;
		int startPoint = x;
		if (hasBlocks(startPoint - 1)) {
			startPoint++;
		}
		if (hasBlocks(startPoint+gapSize))
		{
			gapSize--;
		}		
		if (startPoint == 0) {
			startPoint = startPoint + 2;
		}
		for (int j = startPoint; j < startPoint + gapSize; j++) {
			// System.out.println("Adding GAP at "+j+","+y);
			if (y != 20) 
				for (int k = 0; k < height; k++) {
					setBlock(j, k, (byte) (0 + 0 * 16));
				}
		}
	}

	private void mBuildGap(int x, int y, int size) {
		if (hasBlocks(x - 1)) {
			x++;
		}
		for (int j = x; j < x + size; j++) {
			// System.out.println("Adding GAP at "+j+","+y);
				for (int k = 0; k < height; k++) {
					setBlock(j, k, (byte) (0 + 0 * 16));
				}
		}
	}

	private void mBuildFancyGap(int x, int y) {
		if (hasBlocks(x - 1)) {
			x++;
		}
		int stairSize = random.nextInt(2) + 1;
		int gapSize = random.nextInt(3) + 1;
		mBuildStairs(x, y + 2, stairSize, false);
		mBuildGap(x + stairSize, y, gapSize);
		mBuildStairs(x + gapSize + stairSize, y, stairSize, true);
	}

	private void mBuildFancyGap2(int x, int y, int stairSize, int gapSize) {
		if (hasBlocks(x - 1)) {
			x++;
		}
		mBuildStairs(x, y + 2, stairSize, false);
		mBuildGap(x + stairSize, y, gapSize);
		mBuildStairs(x + gapSize + stairSize, y, stairSize, true);
	}
	//TODO
	private void mBuildHillRange(int x,int y, ArrayList<ArrayList<Integer>>heightMaps,int startX, int size){
		//GLITCHES MAY OCCUR HERE TOO
		ArrayList<Integer> theIndexes = new ArrayList<Integer>();
		ArrayList<Integer> xOffset = new ArrayList<Integer>();
		ArrayList<Integer> hillSize = new ArrayList<Integer>();
		ArrayList<Integer> heights = new ArrayList<Integer>();
		for(int i=0; i<heightMaps.size(); i++){
			if(heightMaps.get(i).size()>=startX+size){
				theIndexes.add(i);
			}
		}
		int rand = random.nextInt(theIndexes.size());
		ArrayList<Integer> hm = heightMaps.get(rand);
		boolean isCapable = false;
		if(hm.size()>startX+size){
			isCapable= true;
		}
		if(isCapable){
			int tempY = hm.get(startX);
			int hC = 0;
			for(int i=startX; i<startX+size; i++){
				if(hm.get(i)==tempY){
					hC++;
				}
				else{
					xOffset.add(i-hC);
					hillSize.add(hC);
					heights.add(hm.get(i));
					hC=0;
				}
				tempY = hm.get(i);
			}
			int xa = startX;
		  for(int i=0; i<heights.size();i++){		
			 System.out.println(i+" S="+hillSize.get(i)+"X="+xOffset.get(i));
			 if(hillSize.get(i)>0){
				 mBuildHills(xOffset.get(i),y,hillSize.get(i),heights.get(i));
				 mSetBlocks(xOffset.get(i),y+heights.get(i),hillSize.get(i)-1);
				 if(heights.get(i)>=4){
				 mSetBlocks(xOffset.get(i),y,hillSize.get(i)/2);}
			 }
		  }
		}
		else{
			rand = random.nextInt(size+1);
			for(int k=x; k<x+size; k+=rand){
				mBuildHills(k,y,rand,random.nextInt(4)+2);
				rand = random.nextInt(size+1);
			}
		}
	}
	private ArrayList<Integer> mBuildNormalizedHeightMap(
			ArrayList<LayeredNote> music) {
		ArrayList<Integer> scaledData = new ArrayList<Integer>();
		ArrayList<Integer> terrain = new ArrayList<Integer>();
		// First Sweep
		int min = 128;
		int max = 0;
		int oldnote = 0;
		for (LayeredNote layer : music) {
			int maxdiff = -1;
			byte NoteValue = layer.getNote(0).getValue();
			for (Note note : layer.getSimultaneousNotes()) {
				byte temp = note.getValue();
				if (Math.abs(oldnote - temp) > maxdiff) {
					maxdiff = Math.abs(oldnote - NoteValue);
					NoteValue = temp;
				}
			}

			if (NoteValue < min && NoteValue != 0)
				min = NoteValue;
			if (NoteValue > max)
				max = NoteValue;
			oldnote = NoteValue;
		}

		// second sweep to store scaled data
		long timelimit = 100;
		int count = 0;
		int sum = 0;
		for (LayeredNote layer : music) {
			byte NoteValue = layer.getNote(0).getValue();
			long NoteTime = layer.getTime();

			if (NoteTime <= timelimit) {
				sum += NoteValue;
				count++;
			} else {
				do {
					if (count == 0)
						count++;
					int avgnote = sum / count;
					int scldData = 1 + (int) ((height - 7)
							* (float) (avgnote - min) / (float) (max - min));
					if (scldData <= 0)
						scldData = 1;
					scaledData.add(scldData);
					timelimit += 100;
					count = 1;
					sum = NoteValue;
				} while (NoteTime > timelimit);
			}
		}

		// smooth out scaled data
		int AveragingTime = 15;
		for (int i = AveragingTime / 2 + 1; i < scaledData.size(); i++) {
			sum = 0;
			for (int j = (i - AveragingTime / 2); j <= (i + AveragingTime / 2); j++) {
				if (j == scaledData.size())
					break;
				if (j < 0)
					j = 0;
				sum += scaledData.get(j);
			}
			terrain.add((int) ((float) sum / (float) AveragingTime));
		}
		return terrain;
	}

	private boolean hasBlocks(int x) {
		boolean hb = false;
		for (int y = 0; y < height; y++) {
			if (getBlock(x, y) == BLOCK_EMPTY
					|| getBlock(x, y) == BLOCK_POWERUP
					|| getBlock(x, y) == BLOCK_COIN) {
				hb = true;
			}
		}
		return hb;
	}

	private void mBuildFlat(ArrayList<Integer> x, ArrayList<Integer> y,
			ArrayList<Integer> sz, ArrayList<ArrayList<Integer>> heightMaps) {
		for (int i = 0; i < x.size(); i++) {
			int index = x.get(i);
			if(index <3){
				index+=3;
			}
			int h = y.get(index);
			if (sz.get(i) >= 10 && sz.get(i) < 20) {
				mBuildFancyGap(index + random.nextInt(sz.get(i) / 2), h);
			} else if (sz.get(i) >= 20 && sz.get(i) < 30) {
				int range = sz.get(i);
				mBuildHillRange(index,h,heightMaps,index,range);
				
			} else if (sz.get(i) < 10) {
				if (sz.get(i) >= 5) {
					mBuildSmallGap(index+5, h);
				}
				else{
					int prob = random.nextInt(10);
					//Small gaps not necessarily flats , can be steps, a fun Bernoulli trial might add a pipe for some challenge
					if(prob > 7 && sz.get(i)>=3){
					mBuildPipes(index,h,random.nextInt(2)+3);}
				}
			} else {
				System.out.println("Greater than 30 Gap Block with size="+ sz.get(i));
				int range = sz.get(i);
				int rand = random.nextInt(range/2);
				int tempRand = rand; 
				for(int k=index; k<index+range; k+=rand){
					if(rand!=tempRand){
					System.out.println("K="+k);
					if(rand <=5 && rand >=3){
						System.out.println("Here be pipes");
						mBuildPipes(k,h,random.nextInt(2)+3);	
					}
					else if(rand >5 && rand <=10){
						System.out.println("Here be mountains");
						 int he = random.nextInt(4)+1;
						 mBuildHills(k,h,rand,he);
						 mSetBlocks(k,h+he,rand-1);
					}
					else if(rand > 10 && rand <=20){
						System.out.println("Here be fancy gaps");
						mBuildFancyGap(k,h);
					}
					else{
						//GLITCHES MAY OCCUR HERE
						for(int l=0; l<rand; l+=10){
							int randomChoice = random.nextInt(3);
							if(randomChoice == 1){
								mBuildPipes(k+l,h,random.nextInt(3)+2);
							}
							else if(randomChoice == 2){
								mBuildFancyGap(k+l,h);
							}
							else{
							 int he = random.nextInt(6)+1;
							 int wid = random.nextInt(5)+3;
							 mBuildHills(k,h,wid,he);
							 mSetBlocks(k,h+he,wid-1);
							}
							
						}
					}
					tempRand = rand;}
					rand = random.nextInt(range/2);
				}
			}
		}

	}

	private void fixWalls() {
		boolean[][] blockMap = new boolean[width + 1][height + 1];

		for (int x = 0; x < width + 1; x++) {
			for (int y = 0; y < height + 1; y++) {
				int blocks = 0;
				for (int xx = x - 1; xx < x + 1; xx++) {
					for (int yy = y - 1; yy < y + 1; yy++) {
						if (getBlockCapped(xx, yy) == GROUND) {
							blocks++;
						}
					}
				}
				blockMap[x][y] = blocks == 4;
			}
		}
		blockify(this, blockMap, width + 1, height + 1);
	}

	private void blockify(Level level, boolean[][] blocks, int width, int height) {
		int to = 0;
		if (type == LevelInterface.TYPE_CASTLE) {
			to = 4 * 2;
		} else if (type == LevelInterface.TYPE_UNDERGROUND) {
			to = 4 * 3;
		}

		boolean[][] b = new boolean[2][2];

		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				for (int xx = x; xx <= x + 1; xx++) {
					for (int yy = y; yy <= y + 1; yy++) {
						int _xx = xx;
						int _yy = yy;
						if (_xx < 0)
							_xx = 0;
						if (_yy < 0)
							_yy = 0;
						if (_xx > width - 1)
							_xx = width - 1;
						if (_yy > height - 1)
							_yy = height - 1;
						b[xx - x][yy - y] = blocks[_xx][_yy];
					}
				}

				if (b[0][0] == b[1][0] && b[0][1] == b[1][1]) {
					if (b[0][0] == b[0][1]) {
						if (b[0][0]) {
							level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
						} else {
							// KEEP OLD BLOCK!
						}
					} else {
						if (b[0][0]) {
							// down grass top?
							level.setBlock(x, y, (byte) (1 + 10 * 16 + to));
						} else {
							// up grass top
							level.setBlock(x, y, (byte) (1 + 8 * 16 + to));
						}
					}
				} else if (b[0][0] == b[0][1] && b[1][0] == b[1][1]) {
					if (b[0][0]) {
						// right grass top
						level.setBlock(x, y, (byte) (2 + 9 * 16 + to));
					} else {
						// left grass top
						level.setBlock(x, y, (byte) (0 + 9 * 16 + to));
					}
				} else if (b[0][0] == b[1][1] && b[0][1] == b[1][0]) {
					level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
				} else if (b[0][0] == b[1][0]) {
					if (b[0][0]) {
						if (b[0][1]) {
							level.setBlock(x, y, (byte) (3 + 10 * 16 + to));
						} else {
							level.setBlock(x, y, (byte) (3 + 11 * 16 + to));
						}
					} else {
						if (b[0][1]) {
							// right up grass top
							level.setBlock(x, y, (byte) (2 + 8 * 16 + to));
						} else {
							// left up grass top
							level.setBlock(x, y, (byte) (0 + 8 * 16 + to));
						}
					}
				} else if (b[0][1] == b[1][1]) {
					if (b[0][1]) {
						if (b[0][0]) {
							// left pocket grass
							level.setBlock(x, y, (byte) (3 + 9 * 16 + to));
						} else {
							// right pocket grass
							level.setBlock(x, y, (byte) (3 + 8 * 16 + to));
						}
					} else {
						if (b[0][0]) {
							level.setBlock(x, y, (byte) (2 + 10 * 16 + to));
						} else {
							level.setBlock(x, y, (byte) (0 + 10 * 16 + to));
						}
					}
				} else {
					level.setBlock(x, y, (byte) (0 + 1 * 16 + to));
				}
			}
		}
	}

	public MyLevel clone() throws CloneNotSupportedException {

		MyLevel clone = new MyLevel(width, height);

		clone.xExit = xExit;
		clone.yExit = yExit;
		byte[][] map = getMap();
		SpriteTemplate[][] st = getSpriteTemplate();

		for (int i = 0; i < map.length; i++)
			for (int j = 0; j < map[i].length; j++) {
				clone.setBlock(i, j, map[i][j]);
				clone.setSpriteTemplate(i, j, st[i][j]);
			}
		clone.BLOCKS_COINS = BLOCKS_COINS;
		clone.BLOCKS_EMPTY = BLOCKS_EMPTY;
		clone.BLOCKS_POWER = BLOCKS_POWER;
		clone.ENEMIES = ENEMIES;
		clone.COINS = COINS;

		return clone;

	}

}